from ma.commons.core.abstractreducer import AbstractReducer


class Reduce(AbstractReducer):    
    """This class is an averaging reduce. It expects a tuple in place of each
    value, where the first index holds the actual average value, and the second
    index holds the weight that needs to be given for that specific tuple (or 
    the number of instances from which this avg has come from) 
    """
    
    def __init__(self, key, list_of_values, key_value_dict, user_vars):     
        """Make an estimatable reducer
        """
        
        AbstractReducer.__init__(self, key, list_of_values, key_value_dict, estimatable=AbstractReducer.ESTIMATOR)
        
        # set the user variables
        self.change_margin = user_vars[0]
        self.if_changed = user_vars[1]
    
    
    def reduce(self, key, list_of_values):    
        """key is the key being reduced and list of values is the 
        lists within a list that need ot be reduced for that key
        if len(list_of_values) is one 
        this reduce counts frequency of key  over the list_of_values
        """
        min = 99999999999
        max = -99999999999
        self.frequency_count =  0
        
        no_inps = 0
        sum = 0.0
        for val in list_of_values:
            no_inps += val[1]
            sum += val[0] * val[1]
            if val[0] > max:
                max = val[0]
            elif val[0] < min:
                min = val[0]
            
        avg = sum / no_inps
        value = [(avg, no_inps)]
        
        self.emit(key, value)
        
        # user calls the if changed function to see if the value has changed 
        # significantly of the key in question, if a change margin is given
        if self.change_margin != None:
            self.if_changed[0] = self.check_if_changed(min, max, self.change_margin)
        
    
    def check_if_changed(self, min, max, margin):
        """This function checks if the value has changed more than a given
        margin. This is done by checking if the range is under a certain
        limit
        """
        
        # if the xml provided no margin value, then the code supposes that
        # the value has always changes during the computation
        if margin == None:
            return True
        
        range = max - min
        change = float(max) * margin 
        min_val_allowed = max - change
        if min < min_val_allowed:
            return True
        else:
            return False
        